from enum import Enum, auto
from typing import Type
from datetime import datetime

from django.core.handlers.wsgi import WSGIRequest
from django.db.models import QuerySet

from jqexception.jqexecption import ValueErr, UnknowErr, ReqMethodErr


def join_filters(query_set, request, filters, by=None):
    """
    链接过滤器
    """
    _query_set = query_set
    for key, value in filters.items():
        if by is not None:
            if by.count(key) == 0:
                continue
        _query_set = value.filter(_query_set, key, request)
    return _query_set


class FilterResolver:
    def __init__(self, field_name=None, is_extra=False, required=False):
        self.field_name = field_name
        self.is_extra = is_extra
        self.required = required

    def get_table_key(self, filter_key):
        return filter_key if self.field_name is None else self.field_name

    def supports_parameter(self, filter_key: str, request: WSGIRequest):
        """默认实现 判断请求中是否存在该字段"""
        return filter_key in request.GET.keys()

    def resolve_argument(self, filter_key: str, request: WSGIRequest):
        """
        需要自己实现
        Returns
        可以返回一个对象或者一个字典
        """
        return None

    def filter(
        self,
        query_set: QuerySet,
        filter_key: str,
        request: WSGIRequest,
    ):
        if not self.supports_parameter(filter_key, request):
            if self.required:
                raise ReqMethodErr
            return query_set
        obj = self.resolve_argument(filter_key, request)

        if obj is None:
            if self.required:
                raise ReqMethodErr
            return query_set

        if isinstance(obj, dict):
            if len(obj) == 0:
                if self.required:
                    raise ReqMethodErr
                return query_set

            _query_set = query_set

            for key, value in obj.items():
                if self.is_extra:
                    _query_set = _query_set.extra(where=[key + "=%s"], params=[value])
                    break
                _query_set = _query_set.filter(**{key: value})
            return _query_set
        _key = self.get_table_key(filter_key)
        if self.is_extra:
            return query_set.extra(where=[_key + "=%s"], params=[obj])

        return query_set.filter(**{_key: obj})


class IntFilterResolver(FilterResolver):
    def resolve_argument(self, filter_key: str, request: WSGIRequest):
        value = request.GET.get(filter_key)
        if value is None:
            return None
        if value == "":
            return None
        return int(value)


class StringFilterResolver(FilterResolver):
    def __init__(
        self,
        is_uuid=False,
        field_name=None,
        is_extra=False,
        required=False,
    ):
        self.is_uuid = is_uuid
        super().__init__(
            field_name,
            is_extra,
            required,
        )

    def resolve_argument(self, filter_key: str, request: WSGIRequest):
        value = request.GET.get(filter_key)
        if value is None:
            return None
        str_value = str(value)
        if str_value == "":
            return None
        if self.is_uuid:
            str_value = str_value.replace("-", "")
        return str_value


class BoolFilterResolver(FilterResolver):
    def resolve_argument(self, filter_key: str, request: WSGIRequest):
        value = request.GET.get(filter_key)
        if value is None:
            return None
        if value == "":
            return None
        if value == "0":
            return False
        if value == "1":
            return True
        if value == "true":
            return True
        if value == "false":
            return False
        return bool(value)


class ValueType(Enum):
    name = auto()
    value = auto()


class EnumFilterResolver(FilterResolver):
    def __init__(
        self,
        enum_cls: Type[Enum],
        value_type: ValueType = ValueType.value,
        field_name=None,
        is_extra=False,
        required=False,
    ):
        super().__init__(field_name, is_extra, required)
        self._enum_cls = enum_cls
        self._value_type = value_type

    def resolve_argument(self, filter_key: str, request: WSGIRequest):
        key = request.GET.get(filter_key)
        if key is None:
            return None
        if key == "":
            return None
        if self._value_type == ValueType.name:
            return self._enum_cls[key].name
        key = int(key)
        return self._enum_cls(key).value


class DateTimeFilterResolver(FilterResolver):
    def __init__(
        self,
        __format: str = "%Y-%m-%d %H:%M:%S",
        field_name=None,
        is_extra=False,
        required=False,
    ):
        super().__init__(field_name, is_extra, required)
        self._format = __format

    def resolve_argument(self, filter_key: str, request: WSGIRequest):
        _value = request.GET.get(filter_key)
        if _value is None:
            return None
        if _value == "":
            return None
        _date = datetime.strptime(_value, self._format)

        _table_key = self.get_table_key(filter_key)

        return {
            _table_key + "__year": _date.year,
            _table_key + "__month": _date.month,
            _table_key + "__day": _date.day,
        }


class DateTimeRangeFilterResolver(FilterResolver):
    def __init__(
        self,
        __format: str = "%Y-%m-%d %H:%M:%S",
        start_suffix="_start",
        end_suffix="_end",
        field_name=None,
        is_extra=False,
        required=False,
    ):
        super().__init__(field_name, is_extra, required)
        self._format = __format
        self._start_suffix = start_suffix
        self._end_suffix = end_suffix

    def supports_parameter(self, filter_key: str, request: WSGIRequest):
        (start_key, end_key) = self._get_key(filter_key)

        return start_key in request.GET or end_key in request.GET

    def resolve_argument(self, filter_key: str, request: WSGIRequest):
        (start_key, end_key) = self._get_key(filter_key)

        start_datatime_str = request.GET.get(start_key)
        end_datatime_str = request.GET.get(end_key)

        start_datatime = None
        end_datatime = None

        if (
            start_datatime_str is not None
            and not isinstance(start_datatime_str, list)
            and start_datatime_str != ""
        ):
            start_datatime = datetime.strptime(start_datatime_str, self._format)

        if (
            end_datatime_str is not None
            and not isinstance(end_datatime_str, list)
            and end_datatime_str != ""
        ):
            end_datatime = datetime.strptime(end_datatime_str, self._format)

        _table_key = self.get_table_key(filter_key)

        if start_datatime is not None and end_datatime is not None:
            return {_table_key + "__range": (start_datatime, end_datatime)}
        else:
            if start_datatime is not None:
                return {_table_key + "__gte": start_datatime}

            if end_datatime is not None:
                return {_table_key + "__lt": end_datatime}
        return None

    def _get_key(self, filter_key: str):
        start_key = filter_key + self._start_suffix
        end_key = filter_key + self._end_suffix
        return start_key, end_key


def get_filters(request, filters, by=None):
    value_list = []
    for key, value in filters.items():
        if by is not None:
            if by.count(key) == 0:
                continue
        current_filter = filters[key]
        current_filter_value = current_filter.resolve_argument(key, request)
        value_list.append(current_filter_value)
    return tuple(value_list)
